
#include <BFile>

#include "ReadWrite_bmp.h"

ReadWrite_bmp::ReadWrite_bmp()
{
	_author = "zym";
	_version = "1.0";
	_description = "This is BWE Plugin for reading *.bmp image file, write width libjpeg, thank a lot !";
}
ReadWrite_bmp::~ReadWrite_bmp()
{

}

void ReadWrite_bmp::setOptions(const BString& options)
{
	_options = options;
}
const BString& ReadWrite_bmp::options() const
{
	return _options;
}

const BString& ReadWrite_bmp::author() const
{
	return _author;
}
const BString& ReadWrite_bmp::version() const
{
	return _version;
}
const BString& ReadWrite_bmp::description() const
{
	return _description;
}
const BString& ReadWrite_bmp::message() const
{
	return _message;
}

BObject* ReadWrite_bmp::load(const BString& fileName)
{
	BString name = fileName.name();
	name.remove(name.find('.'), name.size());
	BImage* image = new BImage();
	image->setName(name);
	if (!load(fileName, image))
	{
		delete image;
		return 0;
	}
	image->setFileName(fileName);
	return image;
}
bool ReadWrite_bmp::load(const BString& fileName, BObject* object)
{
	BImage* image = dynamic_cast<BImage*>(object);
	if (!image)
		return false;

	BFile file(fileName);
	if (!file.open(IO_Read))
		return false;

	Head head;

	file.read(head.dentifier, 2);
	if (head.dentifier[0] != 'B' && head.dentifier[1] != 'M')
		return false;

	file.read(head.fileSize);
	file.read(head.reserved);
	file.read(head.dataOffset);

	file.read(head.headerSize);
	if (head.headerSize == 12)/*OS/2 v1*/
	{
		file.read(head.width);
		file.read(head.height);
		file.read(head.planes);
		file.read(head.pixelBits);
	}
	else if (head.headerSize == 40 || head.headerSize == 108 || head.headerSize == 124)/*Windows v3/v4/v5 header*/
	{
		file.read(head.width);
		file.read(head.height);
		file.read(head.planes);
		file.read(head.pixelBits);
		file.read(head.compression);
		file.read(head.dataSize);

		file.read(head.HResolution);
		file.read(head.VResolution);
		file.read(head.colorsInPalette);
		file.read(head.importantColors);
	}
	else
	{
		return false;
	}

	if (head.pixelBits == 4)
	{
		return false;
	}

	unsigned int bytesPerPixel = 0;
	BByteArray palette;
	if (head.pixelBits == 8)
	{
		if (head.colorsInPalette == 0)
			head.colorsInPalette = 1 << head.pixelBits;
		bytesPerPixel = (head.headerSize == 12 ? 3 : 4); // OS/2 v1 stores RGB, else RGBA
		palette.resize(head.colorsInPalette * bytesPerPixel);
		file.read(palette.data(), palette.size());
	}
	else
	{
		if (head.pixelBits == 16)
			bytesPerPixel = 3;
		else if (head.pixelBits == 24 || head.pixelBits == 32)
			bytesPerPixel = head.pixelBits / 8;
	}

	if (head.colorsInPalette > 0)
	{
		image->setFormat(Format_RGB);
		image->setWidth(head.width);
		image->setHeight(head.height);
		BByte* data = image->pixels()->data();
		int pixelSize = head.width * head.height;
		file.seek(head.dataOffset);
		if (head.pixelBits == 8)
		{
			unsigned char index;
			for (int i = 0; i < pixelSize; i++)
			{
				file.read(index);
				data[i * 3 + 0] = palette[index * bytesPerPixel + 2];
				data[i * 3 + 1] = palette[index * bytesPerPixel + 1];
				data[i * 3 + 2] = palette[index * bytesPerPixel + 0];
			}
		}
		if (head.pixelBits == 16)
		{
			unsigned char index;
			for (int i = 0; i < pixelSize; i++)
			{
				file.read(index);
				data[i * 3 + 0] = palette[index * bytesPerPixel + 2];
				data[i * 3 + 1] = palette[index * bytesPerPixel + 1];
				data[i * 3 + 2] = palette[index * bytesPerPixel + 0];
			}
		}
	}
	else
	{
		Format format = Format_None;
		if (head.pixelBits == 8)
			format = Format_Gray;
		else if (head.pixelBits == 16)
			format = Format_Gray_Alpha;
		else if (head.pixelBits == 24)
			format = Format_RGB;
		else if (head.pixelBits == 32)
			format = Format_RGBA;
		else
			return false;

		image->setFileName(fileName);
		image->setFormat(format);
		image->setWidth(head.width);
		image->setHeight(head.height);
		BByteArray* pixels = image->pixels();
		file.seek(head.dataOffset);
		file.read(pixels->data(), pixels->size());

		if (format == Format_RGB)
		{
			BByte* data = pixels->data();
			for (int i = 0; i < pixels->size(); i += 3)
			{
				bSwap(data[i], data[i + 2]);
			}
		}
		if (format == Format_RGBA)
		{
			BByte* data = pixels->data();
			for (int i = 0; i < pixels->size(); i += 4)
			{
				bSwap(data[i], data[i + 2]);
			}
		}

		image->setPixels(pixels);
	}

	image->reverse(Orientation_Vertical);
	file.close();

	return true;
}
bool ReadWrite_bmp::save(const BString& fileName, const BObject* object) const
{
	const BImage* image = dynamic_cast<const BImage*>(object);
	if (!image)
		return false;

	BFile file(fileName);
	if (!file.open(IO_Write))
		return false;

	Format format = image->format();
	const BByteArray* pixels = image->pixels();
	int pixelSize = image->width() * image->height();

	Head head;
	head.dentifier[0] = 'B';
	head.dentifier[1] = 'M';
	head.height = image->height();
	head.width = image->width();
	head.headerSize = 40;
	head.dataOffset = sizeof(head);//54
	head.dataSize = pixels->size();
	head.HResolution = 0;
	head.VResolution = 0;
	head.planes = 1;
	if (format == Format_Gray)
		head.pixelBits = 8;
	else if (format == Format_Gray_Alpha)
		head.pixelBits = 16;
	else if (format == Format_RGB)
		head.pixelBits = 24;
	else if (format == Format_RGBA)
		head.pixelBits = 32;
	else
		return false;

	file.wmove(sizeof(Head));
	file.seek(head.dataOffset);
	file.write(*pixels);
	head.fileSize = file.offset();
	file.seek(0);
	file.write(head);

	file.close();

	return true;
}

BObject* ReadWrite_bmp::read(const BBuffer& buffer)
{
	BImage* image = new BImage();
	if (!read(buffer, image))
	{
		delete image;
		return 0;
	}
	return image;
}
bool ReadWrite_bmp::read(const BBuffer& buffer, BObject* object)
{
	BImage* image = dynamic_cast<BImage*>(object);
	if (!image)
		return false;

	Head head;
	buffer.read(head);

	Format format = Format_None;
	if (head.pixelBits == 8)
		format = Format_Gray;
	else if (head.pixelBits == 24)
		format = Format_RGB;
	else if (head.pixelBits == 32)
		format = Format_RGBA;
	else
		return false;

	int pixelSize = head.width * head.height * head.pixelBits / 8;
	BByteArray* pixels = new BByteArray(pixelSize);
	buffer.read(*pixels);

	image->setFormat(format);
	image->setWidth(head.width);
	image->setHeight(head.height);
	image->setPixels(pixels);

	return true;
}
bool ReadWrite_bmp::write(BBuffer& buffer, const BObject* object) const
{
	const BImage* image = dynamic_cast<const BImage*>(object);
	if (!image)
		return false;

	Format format = image->format();
	const BByteArray* pixels = image->pixels();
	int pixelSize = image->width() * image->height();

	Head head;
	head.dentifier[0] = 'B';
	head.dentifier[1] = 'M';
	head.height = image->height();
	head.width = image->width();
	head.headerSize = 40;
	head.dataOffset = sizeof(head);//54
	head.dataSize = pixels->size();
	head.HResolution = 0;
	head.VResolution = 0;
	head.planes = 1;
	if (format == Format_Gray)
		head.pixelBits = 8;
	else if (format == Format_RGB)
		head.pixelBits = 24;
	else if (format == Format_RGBA)
		head.pixelBits = 32;
	else
		return false;

	buffer.wmove(sizeof(Head));
	buffer.write(*pixels);
	head.fileSize = buffer.offset();
	buffer.seek(0);
	buffer.write(head);

	return true;
}

